#include "UISkin.h"
#include "Point2d_gl.h"

#include <cmath>

namespace Lame3d
{
	// UISkin //
	UISkin::UISkin(const File &f, TextureManager &tm, FontManager &fM, const DisplayManager &dM)
			: config(f), textureManager(tm), fontManager(fM), displayManager(dM), refCount(0),
				name(config["name"].String()), author(config["author"].String()), 
				windowSkin(NULL), fontSkin(NULL), cursorSkin(NULL), buttonSkin(NULL), textBoxSkin(NULL)
	{
	}

	UISkin::~UISkin()
	{
		if(refCount > 0) {
			throw "Deleting a UISkin with nonzero references!"; 
		}
	}
	
	UISkin::Handle UISkin::GetHandle()
	{
		return Handle(*this);
	}
	
	const std::string& UISkin::Name() const
	{
		return name;
	}
	
	const std::string& UISkin::Author() const
	{
		return author;
	}
	
	void UISkin::load()
	{
		loadFont();
		loadCursor();
		loadButton();
		loadWindowSkin();
		loadTextBoxSkin();
	}
	
	void UISkin::loadWindowSkin()
	{
		// Load Window Skin
		Texture &t = textureManager[config["window"].String()];
		float s = 32.0 / float(displayManager.CurrentConfig().Height); //config["window-deco-size"].Float();
		windowSkin = new WindowSkin(t, s);
	}
	
	void UISkin::loadFont()
	{
		// Load font
		Color fontLightColor(
			config["font-light-color-red"].Float(),
			config["font-light-color-green"].Float(),
			config["font-light-color-blue"].Float());
		Color fontDarkColor(
			config["font-dark-color-red"].Float(),
			config["font-dark-color-green"].Float(),
			config["font-dark-color-blue"].Float());
		Font &lightFont = fontManager.Request(
			config["font"].String(),
			fontLightColor,
			config["font-size"].Int());
		Font &darkFont = fontManager.Request(
			config["font"].String(),
			fontDarkColor,
			config["font-size"].Int());
		fontSkin = new FontSkin(lightFont, darkFont);
	}
	
	void UISkin::loadCursor()
	{
		// Load cursor
		Texture &cursorTexture = textureManager[config["cursor"].String()];
		//cursorSkin = new CursorSkin(cursorTexture, config["cursor-size"].Float());
		cursorSkin = new CursorSkin(cursorTexture, 64.0 / float(displayManager.CurrentConfig().Height));
	}
	
	void UISkin::loadButton()
	{
		// Load button
		Texture &buttonNormal = textureManager[config["button-normal"].String()];
		Texture &buttonDown = textureManager[config["button-down"].String()];
		buttonSkin = new ButtonSkin(buttonNormal, buttonDown,
		//	config["button-deco-size"].Float() );
			16.0 / float(displayManager.CurrentConfig().Height));
	}
	
	void UISkin::loadTextBoxSkin()
	{
		// Load TextBox
		Texture &textTexture = textureManager[config["text-box"].String()];
		textBoxSkin = new TextBoxSkin(textTexture,
		//	config["text-box-deco-size"].Float() );
			32.0 / float(displayManager.CurrentConfig().Height));
	}
	
	void UISkin::unload()
	{
		delete cursorSkin;
		delete buttonSkin;
		delete fontSkin;
		delete windowSkin;
		delete textBoxSkin;
		cursorSkin = NULL;
		buttonSkin = NULL;
		fontSkin = NULL;
		windowSkin = NULL;
		textBoxSkin = NULL;
	}
	
	// UISkin :: WindowSkin //
	UISkin::WindowSkin::WindowSkin(Texture &t, float s)
			: windowTexture(t.GetHandle()), size(s)
	{
		// setup window skin
		Point2d	p0(0, 1),
				p1(.25, 1),
				p2(.5, 1),
				p3(.75, 1),
				p4(0, .75),
				p5(.25, .75),
				p6(.5, .75),
				p7(.75, .75),
				p8(0, .5),
				p9(.25, .5),
				p10(.5, .5),
				p11(.75, .5),
				p12(0, .25),
				p13(.25, .25),
				p14(.50, .25),
				p15(.75, .25);
		upperLeft = makeList(p4, p5, p1, p0);
		upper = makeList(p5, p6, p2, p1);
		upperRight = makeList(p6, p7, p3, p2);
		left = makeList(p8, p9, p5, p4);
		center = makeList(p9, p10, p6, p5);
		right = makeList(p10, p11, p7, p6);
		bottomLeft = makeList(p12, p13, p9, p8);
		bottom = makeList(p13, p14, p10, p9);
		bottomRight = makeList(p14, p15, p11, p10);
	}
	
	UISkin::WindowSkin::~WindowSkin()
	{
		glDeleteLists(upperLeft, 1);
		glDeleteLists(upper, 1);
		glDeleteLists(upperRight, 1);
		glDeleteLists(right, 1);
		glDeleteLists(bottomRight, 1);
		glDeleteLists(bottom, 1);
		glDeleteLists(bottomLeft, 1);
		glDeleteLists(left, 1);
		glDeleteLists(center, 1);
	}
	
	void UISkin::WindowSkin::Render(const Point2d &dims)
	{
		int tileWidth = int(ceilf(dims[0]/size));
		int tileHeight = int(ceilf(dims[1]/size));
		float residueX = dims[0] - tileWidth * size;
		float residueY = dims[1] - tileHeight * size;
		glPushMatrix();
		drawRow(upperLeft, upper, upperRight, tileWidth, residueX);
		glTranslatef(0, -residueY, 0);
		glTranslatef(0, -size, 0);		
		for(int y = 1; y < tileHeight-1; ++y) {
			drawRow(left, center, right, tileWidth, residueX);
			glTranslatef(0, -size, 0);
		}
		if(tileHeight - 1 == 0) {
			glTranslatef(0, size, 0);
		}
		drawRow(bottomLeft, bottom, bottomRight, tileWidth, residueX);
		glPopMatrix();
	}
	
	void UISkin::WindowSkin::drawRow(GLuint l, GLuint c, GLuint r, int cols, float residue)
	{
		glPushMatrix();
		glCallList(l);
		glTranslatef(size, 0, 0);
		glTranslatef(residue, 0, 0);
		for(int x=1; x<cols-1; ++x) {
			glCallList(c);
			glTranslatef(size, 0, 0);
		}
		glCallList(r);
		glPopMatrix();
	}
	
	GLuint UISkin::WindowSkin::makeList(const Point2d &lowerLeft,
			const Point2d &lowerRight,
			const Point2d &upperRight,
			const Point2d &upperLeft)
	{
		GLuint list;
		list = glGenLists(1);
		if(list == 0) {
			throw "Couldn't create display list for WindowSkin. boo!";
		}
		glNewList(list, GL_COMPILE);
		windowTexture.Bind2d();
		glBegin(GL_QUADS);
		// continuing my unhealthy obsession with squares...
		glTexCoord(upperLeft);
		glVertex2f(0, 0);
		glTexCoord(lowerLeft);
		glVertex2f(0, -size);
		glTexCoord(lowerRight);
		glVertex2f(size, -size);
		glTexCoord(upperRight);
		glVertex2f(size, 0);		
		glEnd();		
		glEndList();
		return list;
	}
	
	// UISkin :: FontSkin //
	UISkin::FontSkin::FontSkin(Font &l, Font &d)
			: light(l.GetHandle()), dark(d.GetHandle())
	{
	}
	
	UISkin::FontSkin::~FontSkin()
	{
	}
	
	Font::Handle& UISkin::FontSkin::Light()
	{
		return light;
	}
	
	Font::Handle& UISkin::FontSkin::Dark()
	{
		return dark;
	}
	
	// UISkin :: CursorSkin //
	UISkin::CursorSkin::CursorSkin(Texture &c, float s)
			: cursor(c.GetHandle()), size(s/2.0)
	{
		displayList = glGenLists(1);
		if(displayList == 0) {
			throw "Error creating display list for cursor!";
		}
		
		// compile list
		glNewList(displayList, GL_COMPILE);
		cursor.Bind2d();
		glBegin(GL_QUADS);
		//cursor.Bind2d();
		glPushMatrix();
		//glTranslatef(0, 0, .9);
		glTexCoord2f(0, 1);
		glVertex2f(-size, size);
		glTexCoord2f(0, 0);
		glVertex2f(-size, -size);
		glTexCoord2f(1, 0);
		glVertex2f(size, -size);
		glTexCoord2f(1, 1);
		glVertex2f(size, size);
		glPopMatrix();
		glEnd();
		glEndList(); 
	}
	
	UISkin::CursorSkin::~CursorSkin()
	{
		glDeleteLists(displayList, 1);
	}
	
	void UISkin::CursorSkin::Render()
	{
		glCallList(displayList);
	}
	
	// UISkin :: ButtonSkin //
	UISkin::ButtonSkin::ButtonSkin(Texture &u, Texture &d, float size)
			: buttonDown(d, size), buttonUp(u, size)
	{
	}
	
	UISkin::ButtonSkin::~ButtonSkin()
	{
	}
	
	void UISkin::ButtonSkin::DrawUp(const Point2d &dims)
	{
		buttonUp.Render(dims);
	}
	
	void UISkin::ButtonSkin::DrawDown(const Point2d &dims)
	{
		buttonDown.Render(dims);
	}
	
	// UISkin :: TextBoxSkin //
	UISkin::TextBoxSkin::TextBoxSkin(Texture &t, float s)
			: textBoxSkin(t,s)
	{
	}
	
	UISkin::TextBoxSkin::~TextBoxSkin()
	{
	}
	
	void UISkin::TextBoxSkin::Draw(const Point2d &dims)
	{
		textBoxSkin.Render(dims);
	}
	
	// UISkin :: Handle //
	UISkin::Handle::Handle(UISkin &sk)
			: skin(&sk)
	{
		bind();
	}
	
	UISkin::Handle::~Handle()
	{
		unbind();
	}
	
	const UISkin& UISkin::Handle::Skin() const
	{
		return *skin;
	}
	
	void UISkin::Handle::DrawCursor()
	{
		skin->cursorSkin->Render();
	}
	
	void UISkin::Handle::DrawTextLight(const std::string &text, float width, bool wrap)
	{
		//skin->fontSkin->Light().Print(text, width, wrap);
		FormattedString fStr(text, skin->fontSkin->Light(), width, 1, -1, wrap);
		fStr.Render();
	}
	
	void UISkin::Handle::DrawTextDark(const std::string &text, float width, bool wrap)
	{
		//skin->fontSkin->Dark().Print(text, width, wrap);
		FormattedString fStr(text, skin->fontSkin->Dark(), width, 1, -1, wrap);
		fStr.Render();
	}
	
	void UISkin::Handle::DrawWindow(const Window &w)
	{
		skin->windowSkin->Render(
			w.Size() );
	}
	
	void UISkin::Handle::DrawLabel(const UILabel &l)
	{
		if(l.Style() == UILabel::DARK) {
			/*DrawTextDark(
				l.Text(),
				l.Size()[0],
				l.Wrap() ); */
			//skin->fontSkin->Dark().Print(l.Text(), l.Size());
			FormattedString fStr(l.Text(), skin->fontSkin->Dark(), l.Size()[0], l.Size()[1]);
			fStr.Render();
		} else if(l.Style() == UILabel::LIGHT) {
			/*DrawTextLight(
				l.Text(),
				l.Size()[0],
				l.Wrap() ); */
			//skin->fontSkin->Light().Print(l.Text(), l.Size());
			FormattedString fStr(l.Text(), skin->fontSkin->Dark(), l.Size()[0], l.Size()[1]);
		} else {
			throw "Didn't understand UILabel style when rendering (UISkin)";
		} 
	}
	
	void UISkin::Handle::DrawButton(const UIButton &b)
	{
		if(b.State() == UIButton::UP) {
			skin->buttonSkin->DrawUp( b.Size() );
			
			// draw accompanying text
			Point2d p(
				b.Size()[0] - skin->fontSkin->Dark().Width(b.Text()),
				b.Size()[1] - skin->fontSkin->Dark().LineHeight() );
			
			FormattedString fStr(b.Text(), skin->fontSkin->Dark(), b.Size()[0], b.Size()[1], -1, false);
			//p[1] -= fStr.Height() / 2.0;
			p /= 2.0;
			
			glPushMatrix();
			glTranslatef(p[0], -p[1], .01);
			
			fStr.Render();
			
			glPopMatrix();
			
		} else if(b.State() == UIButton::DOWN) {
			skin->buttonSkin->DrawDown( b.Size() );
			
			// draw text
			Point2d p(
				b.Size()[0] - skin->fontSkin->Light().Width(b.Text()),
				b.Size()[1] - skin->fontSkin->Light().LineHeight() );
			
			FormattedString fStr(b.Text(), skin->fontSkin->Light(), b.Size()[0], b.Size()[1], -1, false);
			//p[1] -= fStr.Height() / 2.0;
			p /= 2.0;
			
			glPushMatrix();
			glTranslatef(p[0], -p[1], .01);
			
			fStr.Render();
						
			glPopMatrix();
		} else {
			throw "Didn't understand UIButton state when rendering (UISkin)";
		}
	}
	
	void UISkin::Handle::DrawTextEntry(UITextEntry &e)
	{
		skin->textBoxSkin->Draw( e.Size() );
		FormattedString fStr(e.TextWithCursor(), skin->fontSkin->Light(), e.Size()[0] - .04, skin->fontSkin->Light().LineHeight(), e.CursorIndex(), false);
		glTranslatef(.01, -(e.Size()[1] - skin->fontSkin->Light().LineHeight()) / 2.0, 0); 
		fStr.Render();
	}
	
	UISkin::Handle& UISkin::Handle::operator=(const UISkin::Handle &h) 
	{
		unbind();
		skin = h.skin;
		bind();
		return *this;
	}
	
	void UISkin::Handle::bind()
	{
		if(skin->refCount++ < 1) {
			skin->load();
		}
		printf("Skin %s refcount: %d\n", skin->Name().c_str(), skin->refCount);
	}
	
	void UISkin::Handle::unbind()
	{
		if(--skin->refCount < 1) {
			skin->unload();
		}
		printf("Skin %s refcount: %d\n", skin->Name().c_str(), skin->refCount);
	}
}

// eof //
